﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
using System.Text.RegularExpressions;

namespace Lib.Query.EntityFramework
{
    public static class EntityFrameworkQueryTranslator
    {        
        public static ObjectQuery<T> Translate<T>(this IReadLiteQuery query, ObjectQuery<T> objectSet)
        {
            StringBuilder queryBuilder = new StringBuilder();
            IList<ObjectParameter> paraColl = new List<ObjectParameter>();
            
            foreach (Criteria criteria in query.Criteria)
            {
                switch (criteria.criteriaOperator)
                {
                    case CriteriaOperator.Equal:
                        queryBuilder.Append(String.Format(" {2} it.{0} = @{1}", FixReservedWords(criteria.PropertyName), GetParameterName(criteria.PropertyName, paraColl.Count), query.QueryOperator));
                        break;
                    case CriteriaOperator.GreaterThan:
                        queryBuilder.Append(String.Format(" {2} it.{0} > @{1}", FixReservedWords(criteria.PropertyName), GetParameterName(criteria.PropertyName, paraColl.Count), query.QueryOperator));
                        break;
                    case CriteriaOperator.GreaterThanEqual:
                        queryBuilder.Append(String.Format(" {2} it.{0} >= @{1}", FixReservedWords(criteria.PropertyName), GetParameterName(criteria.PropertyName, paraColl.Count), query.QueryOperator));
                        break;
                    case CriteriaOperator.LesserThan:
                        queryBuilder.Append(String.Format(" {2} it.{0} < @{1}", FixReservedWords(criteria.PropertyName), GetParameterName(criteria.PropertyName, paraColl.Count), query.QueryOperator));
                        break;
                    case CriteriaOperator.LesserThanEqual:
                        queryBuilder.Append(String.Format(" {2} it.{0} <= @{1}", FixReservedWords(criteria.PropertyName), GetParameterName(criteria.PropertyName, paraColl.Count), query.QueryOperator));
                        break;
                    case CriteriaOperator.Like:
                        queryBuilder.Append(String.Format(" {2} it.{0} LIKE @{1} + '%'", FixReservedWords(criteria.PropertyName), GetParameterName(criteria.PropertyName, paraColl.Count), query.QueryOperator));
                        break;
                    case CriteriaOperator.NotEqual:
                        queryBuilder.Append(String.Format(" {2} it.{0} != @{1}", FixReservedWords(criteria.PropertyName), GetParameterName(criteria.PropertyName, paraColl.Count), query.QueryOperator));
                        break;
                    default:
                        throw new ApplicationException("No operator defined");
                }

                paraColl.Add(new ObjectParameter(GetParameterName(criteria.PropertyName, paraColl.Count), criteria.Value));
            }
            if (queryBuilder.Length > 0)
            {
                string queryString = queryBuilder.ToString().Substring(query.QueryOperator.ToString().Length + 1);
                objectSet = objectSet.Where(queryString, paraColl.ToArray());
            }

            if (query.OrderByClause != null)
                objectSet = objectSet.OrderBy(String.Format("it.{0} {1}", FixReservedWords(query.OrderByClause.PropertyName), GetOrderDirection(query.OrderByClause.Ascending)));

            if (query.TopN > 0)
            {
                ObjectParameter top = new ObjectParameter("Top", query.TopN);
                objectSet = objectSet.Top(query.TopN.ToString(), top);
            }

            return objectSet;
            
        }

        private static string FixReservedWords(string propertyName)
        {
            string value = Regex.Replace(propertyName, "group", "[Group]", RegexOptions.IgnoreCase);            
            return value;
        }

        private static string GetParameterName(string propertyName, int index)
        {
            return propertyName.Replace(".", "_") + "_" + index.ToString();
        }

        private static string GetOrderDirection(bool ascending)
        {
            return (ascending) ? "asc" : "desc";
        }
    }
}
